/*
 * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package com.sun.corba.se.spi.monitoring;

import java.util.*;

/**
 * <p>
 *
 * @author Hemanth Puttaswamy </p> <p> StatisticsAccumulator accumulates the samples provided by the
 *         user and computes the value of minimum, maximum, sum and sample square sum. When the
 *         StatisticMonitoredAttribute calls getValue(), it will compute all the statistics for the
 *         collected samples (Which are Minimum, Maximum, Average, StandardDeviation) and provides a
 *         nice printable record as a String.
 *
 *         Users can easily extend this class and provide the implementation of toString() method to
 *         format the stats as desired. By default all the stats are printed in a single line. </p>
 */
public class StatisticsAccumulator {

  ///////////////////////////////////////
  // attributes


  // Users can extend this class to get access to current Max value
  protected double max = Double.MIN_VALUE;

  // Users can extend this class to get access to current Min value
  protected double min = Double.MAX_VALUE;

  private double sampleSum;

  private double sampleSquareSum;

  private long sampleCount;

  protected String unit;

  ///////////////////////////////////////
  // operations


  /**
   * <p>
   * User will use this method to just register a sample with the
   * StatisticsAccumulator. This is the only method that User will use to
   * expose the statistics, internally the StatisticMonitoredAttribute will
   * collect the information when requested from the ASAdmin.
   * </p>
   * <p>
   *
   * </p>
   * <p>
   *
   * @param value a double value to make it more precise </p>
   */
  public void sample(double value) {
    sampleCount++;
    if (value < min) {
      min = value;
    }
    if (value > max) {
      max = value;
    }
    sampleSum += value;
    sampleSquareSum += (value * value);
  } // end sample


  /**
   * Computes the Standard Statistic Results based on the samples collected
   * so far and provides the complete value as a formatted String
   */
  public String getValue() {
    return toString();
  }

  /**
   * Users can extend StatisticsAccumulator to provide the complete
   * Stats in the format they prefer, if the default format doesn't suffice.
   */
  public String toString() {
    return "Minimum Value = " + min + " " + unit + " " +
        "Maximum Value = " + max + " " + unit + " " +
        "Average Value = " + computeAverage() + " " + unit + " " +
        "Standard Deviation = " + computeStandardDeviation() + " " + unit +
        " " + "Samples Collected = " + sampleCount;
  }

  /**
   * If users choose to custom format the stats.
   */
  protected double computeAverage() {
    return (sampleSum / sampleCount);
  }


  /**
   * We use a derived Standard Deviation formula to compute SD. This way
   * there is no need to hold on to all the samples provided.
   *
   * The method is protected to let users extend and format the results.
   */
  protected double computeStandardDeviation() {
    double sampleSumSquare = sampleSum * sampleSum;
    return Math.sqrt(
        (sampleSquareSum - ((sampleSumSquare) / sampleCount)) / (sampleCount - 1));
  }

  /**
   * <p>
   * Construct the Statistics Accumulator by providing the unit as a String.
   * The examples of units are &quot;Hours&quot;, &quot;Minutes&quot;,
   * &quot;Seconds&quot;, &quot;MilliSeconds&quot;, &quot;Micro Seconds&quot;
   * etc.,
   * </p>
   * <p>
   *
   * @param unit a String representing the units for the samples collected </p>
   * @return a StatisticsAccumulator with ... </p> <p>
   */
  public StatisticsAccumulator(String unit) {
    this.unit = unit;
    sampleCount = 0;
    sampleSum = 0;
    sampleSquareSum = 0;
  }


  /**
   * Clears the samples and starts fresh on new samples.
   */
  void clearState() {
    min = Double.MAX_VALUE;
    max = Double.MIN_VALUE;
    sampleCount = 0;
    sampleSum = 0;
    sampleSquareSum = 0;
  }

  /**
   * This is an internal API to test StatisticsAccumulator...
   */
  public void unitTestValidate(String expectedUnit, double expectedMin,
      double expectedMax, long expectedSampleCount, double expectedAverage,
      double expectedStandardDeviation) {
    if (!expectedUnit.equals(unit)) {
      throw new RuntimeException(
          "Unit is not same as expected Unit" +
              "\nUnit = " + unit + "ExpectedUnit = " + expectedUnit);
    }
    if (min != expectedMin) {
      throw new RuntimeException(
          "Minimum value is not same as expected minimum value" +
              "\nMin Value = " + min + "Expected Min Value = " + expectedMin);
    }
    if (max != expectedMax) {
      throw new RuntimeException(
          "Maximum value is not same as expected maximum value" +
              "\nMax Value = " + max + "Expected Max Value = " + expectedMax);
    }
    if (sampleCount != expectedSampleCount) {
      throw new RuntimeException(
          "Sample count is not same as expected Sample Count" +
              "\nSampleCount = " + sampleCount + "Expected Sample Count = " +
              expectedSampleCount);
    }
    if (computeAverage() != expectedAverage) {
      throw new RuntimeException(
          "Average is not same as expected Average" +
              "\nAverage = " + computeAverage() + "Expected Average = " +
              expectedAverage);
    }
    // We are computing Standard Deviation from two different methods
    // for comparison. So, the values will not be the exact same to the last
    // few digits. So, we are taking the difference and making sure that
    // the difference is not greater than 1.
    double difference = Math.abs(
        computeStandardDeviation() - expectedStandardDeviation);
    if (difference > 1) {
      throw new RuntimeException(
          "Standard Deviation is not same as expected Std Deviation" +
              "\nStandard Dev = " + computeStandardDeviation() +
              "Expected Standard Dev = " + expectedStandardDeviation);
    }
  }


} // end StatisticsAccumulator
